//
//  CheckInController.swift
//  hello
//
//  Created by 赵康 on 2025/1/27.
//

import Fluent
import Vapor
import Foundation
import Logging

// 添加向下兼容的日期格式化辅助函数
private func formatDate(_ date: Date) -> String {
  let formatter = DateFormatter()
  formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
  formatter.timeZone = TimeZone(secondsFromGMT: 0)
  return formatter.string(from: date)
}

struct CheckInController: RouteCollection {
  func boot(routes: any RoutesBuilder) throws {
    let checkIns = routes.grouped("\(RouteEnum.checkIns.rawValue)")
    let protected = checkIns.grouped(AuthMiddleware())

    protected.get("\(RouteEnum.monthly.rawValue)", use: getMonthlyStats)
    protected.post("\(RouteEnum.makeup.rawValue)", use: makeupCheckIn)
    protected.post("\(RouteEnum.makeupWithPoints.rawValue)", use: makeupCheckInWithPoints)
    protected.post("\(RouteEnum.dailyGoal.rawValue)", use: setDailyGoal)
    protected.get("\(RouteEnum.dailyProgress.rawValue)", use: getDailyProgress)
  }

  // 设置每日目标
  @Sendable
  func setDailyGoal(_ req: Request) async throws -> BasicResponse {
    let userID = try req.authenticatedUserID
    
    let goalRequest = try req.content.decode(User.SetDailyGoal.self)
    
    guard goalRequest.dailyGoal >= 60 else {
      throw Abort(.badRequest, reason: "学习目标不能小于60秒")
    }
    
    guard let user = try await User.find(userID, on: req.db) else {
      throw Abort(.notFound, reason: "用户不存在")
    }
    
    user.dailyGoal = goalRequest.dailyGoal
    try await user.save(on: req.db)
    
    return BasicResponse(
      success: true,
      message: "设置学习目标成功"
    )
  }
  
  // 获取今日学习进度
  @Sendable
  func getDailyProgress(_ req: Request) async throws -> DailyProgressResponse {
    let userID = try req.authenticatedUserID
    
    req.logger.info("开始获取用户 \(userID) 的每日学习进度")
    
    guard let user = try await User.find(userID, on: req.db) else {
      req.logger.error("用户不存在: \(userID)")
      throw Abort(.notFound, reason: "用户不存在")
    }
    
    // 获取今天的开始时间和结束时间
    let today = Calendar.mondayFirst.startOfDay(for: Date())
    let tomorrow = Calendar.mondayFirst.date(byAdding: .day, value: 1, to: today)!
    
    req.logger.info("今日范围计算: 开始=\(formatDate(today)), 结束=\(formatDate(tomorrow))")
    
    // 获取今日的学习记录
    let histories = try await History.query(on: req.db)
      .filter(\.$user.$id == userID)
      .filter(\.$lastListenedAt >= today)
      .filter(\.$lastListenedAt < tomorrow)
      .filter(\.$listenedDuration > 0)
      .all()
    
    req.logger.info("查询到用户 \(userID) 今日学习记录数量: \(histories.count)")
    
    // 记录每条历史记录的详细信息
    for (index, history) in histories.enumerated() {
      let audioID = history.$audio.id
      let lastListenedTime = history.lastListenedAt != nil ? formatDate(history.lastListenedAt!) : "nil"
      req.logger.info("记录[\(index+1)]: ID=\(history.id?.uuidString ?? "nil"), 音频ID=\(audioID), 时长=\(history.listenedDuration)秒, 最后听取时间=\(lastListenedTime), 删除状态=\(history.deleteStatus.rawValue)")
    }
    
    // 计算今日总听力时长
    let totalDuration = histories.reduce(0) { $0 + $1.listenedDuration }
    
    req.logger.info("用户 \(userID) 今日总学习时长: \(totalDuration)秒 (约\(Int(totalDuration/60))分\(Int(totalDuration.truncatingRemainder(dividingBy: 60)))秒)")
    
    // 判断是否完成今日目标
    let isCompleted = totalDuration >= user.dailyGoal
    
    req.logger.info("用户 \(userID) 每日目标: \(user.dailyGoal)秒, 是否完成: \(isCompleted)")
    
    let response = DailyProgressResponse(
      dailyGoal: user.dailyGoal,
      currentProgress: totalDuration,
      isCompleted: isCompleted
    )
    
    req.logger.info("返回用户 \(userID) 学习进度: 目标=\(response.dailyGoal)秒, 当前=\(response.currentProgress)秒, 完成=\(response.isCompleted)")
    
    return response
  }

  // 获取月度打卡统计
  @Sendable
  func getMonthlyStats(_ req: Request) async throws -> MonthlyCheckInResponse {
    let userID = try req.authenticatedUserID

    guard let yearString = req.query[String.self, at: "year"],
      let monthString = req.query[String.self, at: "month"],
      let year = Int(yearString),
      let month = Int(monthString),
      month >= 1 && month <= 12
    else {
      throw Abort(.badRequest, reason: "Invalid year or month")
    }

    return try await History.getCheckInStats(
      for: userID,
      year: year,
      month: month,
      db: req.db
    )
  }

  // 补签接口
  @Sendable
  func makeupCheckIn(_ req: Request) async throws -> BasicResponse {
    let userID = try req.authenticatedUserID

    let makeup = try req.content.decode(MakeupCheckIn.self)

    // 使用TimeRange获取当前周范围
    let timeRange = TimeRange.week
    let (weekStart, _) = timeRange.getDateRange()

    let makeupDate = Calendar.mondayFirst.startOfDay(for: makeup.date)
    let today = Calendar.mondayFirst.startOfDay(for: Date())

    // 检查补签日期是否在当前自然周内
    guard makeupDate >= weekStart && makeupDate <= today else {
      throw Abort(.badRequest, reason: "只能补签本周内的记录")
    }

    // 获取当前月范围
    let monthTimeRange = TimeRange.month
    let (monthStart, _) = monthTimeRange.getDateRange()

    // 检查本月补签次数
    let makeupCount = try await History.query(on: req.db)
      .filter(\.$user.$id == userID)
      .filter(\.$lastListenedAt >= monthStart)
      .filter(\.$lastListenedAt < today)
      .filter(\.$audio.$id == UUID(uuidString: "00000000-0000-0000-0000-000000000000")!)
      .count()

    guard makeupCount < 4 else {
      throw Abort(.badRequest, reason: "本月补签次数已达上限（4次）")
    }

    // 检查该日期是否已有打卡记录
    let dayEnd = Calendar.mondayFirst.date(byAdding: .day, value: 1, to: makeupDate)!
    let existingCheckIn = try await History.query(on: req.db)
      .filter(\.$user.$id == userID)
      .filter(\.$lastListenedAt >= makeupDate)
      .filter(\.$lastListenedAt < dayEnd)
      .first()

    if existingCheckIn != nil {
      throw Abort(.badRequest, reason: "该日期已有打卡记录")
    }

    // 获取用户每日学习目标
    guard let user = try await User.find(userID, on: req.db) else {
      throw Abort(.notFound, reason: "用户不存在")
    }

    let makeupAudio = try await getOrCreateMakeupAudio(req.db)

    let history = History(
      userID: userID,
      audioID: try makeupAudio.requireID(),
      progress: 1.0,
      duration: user.dailyGoal, // 使用用户设置的每日目标
      isFinished: true,
      lastListenedAt: makeup.date,
      createdAt: Date(),
      listenedDuration: user.dailyGoal, // 使用用户设置的每日目标
      listenStatus: .completed,
      completedCount: 1
    )

    try await history.save(on: req.db)

    // 增加总打卡天数
    user.totalCheckIns += 1
    try await user.save(on: req.db)

    // 更新用户连续打卡天数
    try await updateUserStreak(userID: userID, db: req.db)

    return BasicResponse(
      success: true,
      message: "补签成功"
    )
  }
  
  // 使用积分补签接口
  @Sendable
  func makeupCheckInWithPoints(_ req: Request) async throws -> BasicResponse {
    let userID = try req.authenticatedUserID
    
    let makeup = try req.content.decode(MakeupCheckIn.self)
    
    // 获取用户信息
    guard let user = try await User.find(userID, on: req.db) else {
      throw Abort(.notFound, reason: "用户不存在")
    }
    
    // 检查用户积分是否足够
    guard user.points >= 700 else {
      throw Abort(.badRequest, reason: "积分不足，需要700积分")
    }
    
    let makeupDate = Calendar.mondayFirst.startOfDay(for: makeup.date)
    let today = Calendar.mondayFirst.startOfDay(for: Date())
    
    // 检查补签日期是否在过去
    guard makeupDate < today else {
      throw Abort(.badRequest, reason: "只能补签过去的日期")
    }
    
    // 检查该日期是否已有打卡记录
    let dayEnd = Calendar.mondayFirst.date(byAdding: .day, value: 1, to: makeupDate)!
    let existingCheckIn = try await History.query(on: req.db)
      .filter(\.$user.$id == userID)
      .filter(\.$lastListenedAt >= makeupDate)
      .filter(\.$lastListenedAt < dayEnd)
      .first()
    
    if existingCheckIn != nil {
      throw Abort(.badRequest, reason: "该日期已有打卡记录")
    }
    
    // 扣除积分
    user.points -= 700
    try await user.save(on: req.db)
    
    // 创建积分历史记录
    if #available(macCatalyst 15.0, *) {
      try await createPointsHistory(
        userID: userID,
        amount: -700,
        type: .makeup,
        description: "使用积分补签：\(makeupDate.formatted(date: .long, time: .omitted))",
        db: req.db
      )
    } else {
        // Fallback on earlier versions
    }
    
    // 创建补签记录
    let makeupAudio = try await getOrCreateMakeupAudio(req.db)
    
    let history = History(
      userID: userID,
      audioID: try makeupAudio.requireID(),
      progress: 1.0,
      duration: user.dailyGoal,
      isFinished: true,
      lastListenedAt: makeup.date,
      createdAt: Date(),
      listenedDuration: user.dailyGoal,
      listenStatus: .completed,
      completedCount: 1
    )
    
    try await history.save(on: req.db)
    
    // 更新连续打卡天数和总打卡天数
    user.totalCheckIns += 1
    try await updateUserStreak(userID: userID, db: req.db)
    
    // 更新里程碑奖励
    try await checkAndAwardMilestonePoints(user: user, db: req.db)
    
    return BasicResponse(
      success: true,
      message: "使用积分补签成功，扣除700积分"
    )
  }

  private func updateUserStreak(userID: UUID, db: Database) async throws {
    let logger = Logger(label: "CheckInController.updateUserStreak")
    logger.info("开始更新用户 \(userID) 的连续打卡天数")
    
    let user = try await User.find(userID, on: db)
    guard let user = user else {
      logger.error("用户不存在: \(userID)")
      throw Abort(.notFound)
    }
    
    logger.info("用户当前连续打卡天数: \(user.currentStreak), 总打卡天数: \(user.totalCheckIns)")

    // 获取今天的开始时间
    let today = Calendar.mondayFirst.startOfDay(for: Date())
    logger.info("今天日期: \(formatDate(today))")

    // 获取所有有效的打卡记录（包括补签）
    let allHistories = try await History.query(on: db)
      .filter(\.$user.$id == userID)
      .sort(\.$lastListenedAt, .descending)
      .all()
    
    logger.info("获取到用户所有历史记录: \(allHistories.count) 条")

    // 按日期分组并累加时长
    var dailyListeningTime: [Date: Double] = [:]
    
    for history in allHistories {
      guard let date = history.lastListenedAt else { continue }
      let dayStart = Calendar.mondayFirst.startOfDay(for: date)
      let currentTotal = dailyListeningTime[dayStart] ?? 0
      dailyListeningTime[dayStart] = currentTotal + history.listenedDuration
      
      logger.info("历史记录: 日期=\(formatDate(dayStart)), 音频ID=\(history.$audio.id), 时长=\(history.listenedDuration)秒, 累计=\(dailyListeningTime[dayStart]!)秒")
    }
    
    // 筛选出达到每日目标的日期
    let completedDates = dailyListeningTime.filter { $0.value >= user.dailyGoal }.keys.sorted(by: >)
    
    if !completedDates.isEmpty {
      logger.info("已达成目标的日期数量: \(completedDates.count), 最近日期: \(formatDate(completedDates[0]))")
    } else {
      logger.info("没有达成目标的日期")
    }

    // 如果没有打卡记录，重置连续打卡天数为0
    guard !completedDates.isEmpty else {
      logger.info("无打卡记录，重置连续打卡天数为0")
      user.currentStreak = 0
      try await user.save(on: db)
      return
    }

    // 计算连续打卡天数（从最近一次打卡开始往前数）
    var currentStreak = 1
    let calendar = Calendar.mondayFirst
    var checkDate = completedDates[0]
    
    logger.info("开始计算连续打卡，从最近日期 \(formatDate(checkDate)) 开始")

    // 从最新打卡日期往前检查每一天是否有打卡记录
    while true {
      let previousDate = calendar.date(byAdding: .day, value: -1, to: checkDate)!
      // 检查前一天是否有达到学习目标的打卡记录
      if completedDates.contains(where: { calendar.isDate($0, inSameDayAs: previousDate) }) {
        currentStreak += 1
        checkDate = previousDate
        logger.info("找到前一天 \(formatDate(previousDate)) 的打卡记录，连续天数增加到 \(currentStreak)")
      } else {
        logger.info("前一天 \(formatDate(previousDate)) 无打卡记录，连续计算结束")
        break
      }
    }

    // 检查当前是否还在连续打卡中
    let lastCheckInDate = completedDates[0]
    if let daysSinceLastCheckIn = calendar.dateComponents([.day], from: lastCheckInDate, to: today)
      .day
    {
      logger.info("距最后打卡天数: \(daysSinceLastCheckIn)")
      if daysSinceLastCheckIn > 1 {
        // 如果最后一次打卡不是昨天或今天，说明连续打卡已中断
        logger.info("最后打卡不是昨天或今天，连续打卡已中断，重置为0")
        currentStreak = 0
      }
    }

    // 检查是否是新的一天的打卡，如果是则需要奖励积分
    let shouldAwardPoints = shouldAwardDailyPoints(user: user, today: today)
    logger.info("是否需要奖励积分: \(shouldAwardPoints)")
    
    // 如果需要奖励积分
    if shouldAwardPoints {
      // 增加10积分
      user.points += 10
      
      // 记录最后一次奖励积分的日期
      user.lastPointsAwardedDate = today
      
      // 增加总打卡天数
      user.totalCheckIns += 1
      
      logger.info("奖励10积分，更新后总积分: \(user.points), 总打卡天数: \(user.totalCheckIns)")
      
      // 创建积分历史记录
      try await createPointsHistory(
        userID: user.requireID(),
        amount: 10,
        type: .dailyCheckIn,
        description: "每日打卡奖励",
        db: db
      )
      
      // 检查是否达到里程碑奖励
      try await checkAndAwardMilestonePoints(user: user, db: db)
    }
    
    // 更新总打卡天数以匹配实际完成的打卡数量
    let oldTotalCheckIns = user.totalCheckIns
    user.totalCheckIns = max(user.totalCheckIns, completedDates.count)
    
    let oldStreak = user.currentStreak
    user.currentStreak = currentStreak
    
    logger.info("更新用户数据: 连续打卡 \(oldStreak) -> \(currentStreak), 总打卡 \(oldTotalCheckIns) -> \(user.totalCheckIns)")
    
    try await user.save(on: db)
  }
  
  // 检查是否应该奖励每日积分
  private func shouldAwardDailyPoints(user: User, today: Date) -> Bool {
    guard let lastAwardDate = user.lastPointsAwardedDate else {
      // 如果没有最后奖励日期，说明用户从未获得过积分奖励
      return true
    }
    
    // 检查最后一次奖励的日期是否是今天
    let calendar = Calendar.mondayFirst
    let lastAwardDay = calendar.startOfDay(for: lastAwardDate)
    let todayStart = calendar.startOfDay(for: today)
    
    // 如果最后一次奖励不是今天，则应该奖励积分
    return lastAwardDay != todayStart
  }
  
  // 检查并奖励里程碑积分
  private func checkAndAwardMilestonePoints(user: User, db: Database) async throws {
    // 里程碑天数和对应的奖励积分
    let milestones: [(days: Int, points: Int)] = [
      (7, 100),
      (30, 200),
      (60, 300),
      (100, 600),
      (200, 1000),
      (360, 2000)
    ]
    
    // 计算当前里程碑周期中的天数
    let currentCycleDays = user.totalCheckIns - user.lastMilestoneAwarded
    
    // 检查是否达到里程碑
    for milestone in milestones.sorted(by: { $0.days < $1.days }) {
      if currentCycleDays >= milestone.days {
        // 用户达到了新的里程碑
        
        // 奖励积分
        user.points += milestone.points
        
        // 更新最后里程碑奖励天数
        user.lastMilestoneAwarded += milestone.days
        
        // 保存用户信息
        try await user.save(on: db)
        
        // 创建积分历史记录
        try await createPointsHistory(
          userID: user.requireID(),
          amount: milestone.points,
          type: .milestone,
          description: "累计打卡\(milestone.days)天里程碑奖励",
          db: db
        )
        
        // 只奖励一个里程碑，然后退出
        // 如果用户一次性满足多个里程碑，会在下次打卡时奖励下一个
        break
      }
    }
  }

  private func getOrCreateMakeupAudio(_ db: Database) async throws -> Audio {
    let systemAudioID = UUID(uuidString: "00000000-0000-0000-0000-000000000000")!
    let systemAlbumID = UUID(uuidString: "00000000-0000-0000-0000-000000000000")!

    // 先检查系统专辑是否存在
    if let existingAudio = try await Audio.query(on: db)
      .filter(\.$id == systemAudioID)
      .first()
    {
      return existingAudio
    }

    // 创建系统专辑
    let systemAlbum = Album(
      id: systemAlbumID,
      title: "系统专辑",
      description: "系统生成的专辑",
      cover: nil,
      audioCount: 0,
      playCount: 0,
      favoriteCount: 0,
      wordCount: 0,
      isDeleted: false,
      createdAt: Date(),
      updatedAt: Date(),
      category: .audioBook
    )
    try await systemAlbum.save(on: db)

    // 创建系统补签音频记录
    let makeupAudio = Audio(
      id: systemAudioID,
      title: "补签音频",
      description: "系统生成的补签音频",
      duration: 60,
      cover: nil,
      author: nil,
      url: "system://makeup",
      size: 0,
      isDeleted: false,
      createdAt: Date(),
      updatedAt: Date(),
      albumID: systemAlbumID
    )
    try await makeupAudio.save(on: db)
    return makeupAudio
  }
  
  // 创建积分历史记录的辅助方法
  private func createPointsHistory(
    userID: UUID,
    amount: Int,
    type: PointsHistoryType,
    description: String,
    db: Database
  ) async throws {
    let pointsHistory = PointsHistory(
      userID: userID,
      amount: amount,
      balance: try await getCurrentPointsBalance(userID: userID, db: db),
      type: type,
      description: description,
      createdAt: Date()
    )
    try await pointsHistory.save(on: db)
  }
  
  // 获取当前积分余额
  private func getCurrentPointsBalance(userID: UUID, db: Database) async throws -> Int {
    guard let user = try await User.find(userID, on: db) else {
      throw Abort(.notFound, reason: "用户不存在")
    }
    return user.points
  }
}

struct MakeupCheckIn: Content {
  let date: Date
}

struct DailyProgressResponse: Content {
  let dailyGoal: Double
  let currentProgress: Double
  let isCompleted: Bool
}
